//
//  KHistogram.m
//  ToneMapper
//
//  Created by Andrey on 22/03/2009.
//  Copyright 2009 Karma Software. All rights reserved.
//

#import <QuartzCore/CoreImage.h>
#import "KHistogram.h"
#import "KImage.h"
#import "KPixel.h"

void AddValueToHistogram(NSUInteger* histogramData, 
						 NSUInteger intervalCount, 
						 CGFloat value, 
						 NSUInteger* maxPixelsPerInterval)
{
	NSUInteger interval = (NSUInteger)(intervalCount * value);
	if (interval >= intervalCount)
		interval = intervalCount - 1;
	
	assert(interval < intervalCount);
	
	histogramData[interval]++;
	
	if (histogramData[interval] > *maxPixelsPerInterval)
		*maxPixelsPerInterval = histogramData[interval];
}

@implementation KHistogram

@synthesize intervalCount;
@synthesize channelHistograms;

- (id) initWithIntervalCount: (NSUInteger)numIntervals andChannelCount: (NSUInteger)numChannels
{
	if (self = [super init])
	{
		intervalCount = numIntervals;
		channelCount = numChannels;
		channelHistograms = 
		(KChannelHistogramArray)malloc(sizeof(KChannelHistogram) * numChannels);
		
		NSUInteger channelDataSize = sizeof(NSUInteger) * intervalCount;
		for (int i = 0; i < numChannels; i++)
		{
			channelHistograms[i].data = (NSUInteger*)malloc(channelDataSize);
			bzero(channelHistograms[i].data, channelDataSize);
			channelHistograms[i].maxPixelsPerInterval = 0;
			channelHistograms[i].mean = 0.0;
			channelHistograms[i].variance = 0.0;
		}
	}
	
	return self;
}

- (void) computeForImage: (KImage*)image
{
	for (int i = 0; i < channelCount; i++)
	{
		bzero(channelHistograms[i].data, sizeof(NSUInteger) * intervalCount);
		channelHistograms[i].maxPixelsPerInterval = 0;
		channelHistograms[i].mean = 0.0;
		channelHistograms[i].variance = 0.0;
	}
	
	ForEachImagePixel(image)
	{
		KRedundantColorVector colorVector = 
		[[image pixelAtX:x y:y] redundantColorVector];
		
		for (int i = 0; i < channelCount; i++)
		{
			AddValueToHistogram(channelHistograms[i].data, 
								intervalCount, 
								colorVector.data[i], 
								&channelHistograms[i].maxPixelsPerInterval);
		}
	}

	square = image.width * image.height;
	
	[self computeStatistics];
/*
	[self saveToTikzFile:@"/Users/Andrey/MSTU/Master/Paper/Analytics/LowContrastHistogram.tex" 
			  forChannel: BrightnessImageChannel];
 */
}

- (void) computeStatistics
{
	// Compute means
	for (int i = 0; i < channelCount; i++)
	{
		for (int j = 0; j < intervalCount; j++)
		{
			CGFloat intervalLowBound = (CGFloat)j / intervalCount;
			channelHistograms[i].mean += intervalLowBound * channelHistograms[i].data[j];
		}
		
		channelHistograms[i].mean /= square;
	}
	
	// Compute variances
	for (int i = 0; i < channelCount; i++)
	{
		for (int j = 0; j < intervalCount; j++)
		{
			CGFloat intervalLowBound = (CGFloat)j / intervalCount;
			channelHistograms[i].variance += 
			(intervalLowBound - channelHistograms[i].mean) * 
			(intervalLowBound - channelHistograms[i].mean) * 
			channelHistograms[i].data[j];
		}
		
		channelHistograms[i].variance /= square;
	}
}

- (void) saveToTikzFile: (NSString*)tikzFilename forChannel: (enum KImageChannel)channel
{
	NSString* plotDataFilename = 
	[[tikzFilename stringByDeletingLastPathComponent] stringByAppendingString:@"/PlotData.table"];
	NSString* plotData = [NSString string];
	for (int i = 0; i < intervalCount; i++)
	{
		CGFloat x = i * 3.0 / intervalCount;
		CGFloat y = 
		(CGFloat)channelHistograms[channel].data[i] / 
		channelHistograms[channel].maxPixelsPerInterval;
		
		plotData = 
		[plotData stringByAppendingFormat:@"%.5f\t%.5f\n", x, y];
	}
	[plotData writeToFile:plotDataFilename 
			   atomically:NO 
				 encoding:NSUTF8StringEncoding 
					error:NULL];
	
	NSString* tikzContent = @"\\begin{tikzpicture}[scale = 3, >=stealth]\n";
	// Draw grid
	tikzContent = 
	[tikzContent stringByAppendingString:@"\t\\draw[very thin, color = gray] (-0.1, -0.1) grid (3.1, 1.1);\n"];
	// Draw axis
	tikzContent = 
	[tikzContent stringByAppendingString:@"\t\\draw[->] (-0.1, 0) -- (3.1, 0) node[right] {$c_b$};\n"];
	tikzContent = 
	[tikzContent stringByAppendingString:@"\t\\draw[->] (0, -0.1) -- (0, 1.1) node[above] {$h_b$};\n"];
	tikzContent = 
	[tikzContent stringByAppendingFormat:@"\t\\draw[thick] plot[smooth,ycomb] file {%@};\n", plotDataFilename];
	
	tikzContent = [tikzContent stringByAppendingString:@"\\end{tikzpicture}"];
	[tikzContent writeToFile:tikzFilename 
				  atomically:NO 
					encoding:NSUTF8StringEncoding 
					   error:NULL];
	
}

- (enum KImageChannel) maxVarianceChannel
{
	enum KImageChannel result = 0;
	CGFloat maxValue = 1e-30;
	
	for (int i = 0; i < channelCount / 2; i++)
	{
		if (channelHistograms[i].variance > maxValue)
		{
			maxValue = channelHistograms[i].variance;
			result = i;
		}
	}
	
	return result;
}

- (void) dealloc
{
	for (int i = 0; i < channelCount; i++)
	{
		free(channelHistograms[i].data);
	}
	
	free(channelHistograms);
	
	[super dealloc];
}

@end
